package com.sinosoft.hydra.um.services;

import cn.com.sinux.spring.commons.TreeNodeHelper;
import com.sinosoft.hydra.um.condition.SearchResourceCondition;
import com.sinosoft.hydra.um.dao.SysResourceDao;
import com.sinosoft.hydra.um.domain.model.Site;
import com.sinosoft.hydra.um.domain.permission.SysResource;
import com.sinosoft.hydra.um.domain.permission.SysRole;
import com.sinosoft.hydra.um.domain.permission.SysUserMember;
import cn.com.sinux.spring.exception.BusinessException;
import cn.com.sinux.spring.exception.ServiceException;
import com.sinosoft.hydra.um.helper.AuthContextHelper;
import com.sinosoft.hydra.um.repositories.SysResourceRepository;
import com.sinosoft.hydra.um.repositories.SysRoleRepository;
import com.sinosoft.hydra.um.repositories.SysUserRepository;
import cn.com.sinux.spring.utils.SpecificationHelper;
import cn.com.sinux.spring.utils.TreeNodeUtils;
import cn.com.sinux.spring.utils.ValidatorUtils;;
import com.sinosoft.hydra.um.vo.Node;
import com.sinosoft.hydra.um.vo.Node;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

/**
 * @author liangminga
 * @since 0.5-alpha
 * 2014年12月17日
 * 描述：进行资源的增删查改等操作。验证增加资源时资源名称重复，编辑资源时资源名称重复的功能。以及跳转到用户资源页面。
 */

@Service
public class SysResourceService {
    /**
     * 资源持久化类
     */
    @Autowired
    private SysResourceDao resourceDao;
    /**
     * 资源持久化类
     */
    @Autowired
    private SysResourceRepository resourceRepository;
    /**
     * 系统用户持久化类
     */
    @Autowired
    private SysUserRepository sysUserRepository;

    /**
     * 资源关系持久类
     */
    @Autowired
    private SysResourceRepository sysResourceRepository;
    /**
     * 角色数据层
     */
    @Autowired
    private SysRoleRepository sysRoleRepository;

    /**
     * 分页查找资源列表
     *
     * @param pageable 分页对象
     * @param resource 资源
     * @return Page<SysResource> 页面结果对象
     */
    public Page<SysResource> findAllByPage(Pageable pageable,
                                           final SearchResourceCondition resource) {
        Page<SysResource> page = resourceRepository.findAll(buildSpecification(resource), pageable);
        return page;
    }

    public Specification<SysResource> buildSpecification(SearchResourceCondition searchResourceCondition) {
        final SearchResourceCondition condition = searchResourceCondition == null ? new SearchResourceCondition() : searchResourceCondition;
        return SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysResource>() {

            public void fillParam(Root<SysResource> root, CriteriaBuilder cb, List<Predicate> list) {
                if (StringUtils.isNotBlank(condition.getPermission())) {
                    list.add(cb.equal(root.get("permission").as(String.class), condition.getPermission()));
                }
                if (StringUtils.isNotBlank(condition.getResourceName())) {
                    list.add(
                            condition.isLikeWords()
                                    ? cb.like(root.get("resourceName").as(String.class), "%" + condition.getResourceName() + "%")
                                    : cb.equal(root.get("resourceName").as(String.class), condition.getResourceName()));
                }
                if (StringUtils.isNotBlank(condition.getParentId())) {
                    list.add(cb.equal(root.get("parentId").as(String.class), condition.getParentId()));
                }
                if (StringUtils.isNotBlank(condition.getResourceType())) {
                    list.add(cb.equal(root.get("resourceType").as(String.class), condition.getResourceType()));
                }
                if (StringUtils.isNotBlank(condition.getPermission())) {
                    list.add(cb.like(root.get("permission").as(String.class), "%" + condition.getPermission() + "%"));
                }
                if (ValidatorUtils.isArrayNotEmpty(condition.getFilterIds())) {
                    list.add(root.get("id").as(String.class).in(condition.getFilterIds()).not());
                }
                if (condition.isRoot()) {
                    list.add(cb.equal(root.get("parentId").as(String.class), ""));
                }
                if (condition.getAvailable() != null) {
                    list.add(cb.equal(root.get("available").as(Integer.class), condition.getAvailable()));
                }
                if (StringUtils.isNotBlank(condition.getResourceUrl())) {
                    list.add(cb.like(root.get("resourceUrl").as(String.class), "%" + condition.getResourceUrl() + "%"));
                }
            }
        });
    }

    /**
     * 添加资源
     *
     * @param resource 资源
     * @return 成功或失败数据
     */

    @Transactional
    public SysResource addResource(SysResource resource) {
        if (StringUtils.isBlank(resource.getResourceName()) || StringUtils.isBlank(resource.getPermission())) {
            throw new ServiceException("资源名称和权限不能为空");
        }
        SearchResourceCondition searchResourceCondition = new SearchResourceCondition();
        searchResourceCondition.setResourceName(resource.getResourceName());
        if (resourceRepository.count(buildSpecification(searchResourceCondition)) > 0) {
            throw new BusinessException("zi.yuan.ming.cheng.yi.jing.cun.zai");
        }
        searchResourceCondition = new SearchResourceCondition();
        searchResourceCondition.setResourceName(resource.getPermission());
        if (resourceRepository.count(buildSpecification(searchResourceCondition)) > 0) {
            throw new BusinessException("quan.xian.ma.yi.jing.cun.zai");
        }
        if (StringUtils.isBlank(resource.getResourceUrl())) {
            resource.setResourceUrl("");
        }
        resource.setOrderNo(0);
        resourceRepository.save(resource);
        return resource;
    }

    /**
     * 更新资源
     *
     * @param resource 资源
     * @return 成功或失败数据
     */
    @Transactional
    public SysResource updateResource(SysResource resource) {
        SysResource db = sysResourceRepository.findOne(resource.getId());
        if (db == null) {
            throw new ServiceException("修改的资源部存在");
        }
        if (StringUtils.isBlank(resource.getResourceName()) || StringUtils.isBlank(resource.getPermission())) {
            throw new ServiceException("资源名称和权限不能为空");
        }
        SearchResourceCondition searchResourceCondition = new SearchResourceCondition();
        searchResourceCondition.setResourceName(resource.getResourceName());
        if (!db.getResourceName().equals(resource.getResourceName()) && resourceRepository.count(buildSpecification(searchResourceCondition)) > 0) {
            throw new BusinessException("zi.yuan.ming.cheng.yi.jing.cun.zai");
        }
        searchResourceCondition = new SearchResourceCondition();
        searchResourceCondition.setResourceName(resource.getPermission());
        if (!db.getPermission().equals(resource.getPermission()) && resourceRepository.count(buildSpecification(searchResourceCondition)) > 0) {
            throw new BusinessException("quan.xian.ma.yi.jing.cun.zai");
        }
        resource.setPermission(db.getPermission());
        resource.setResourceType(db.getResourceType());
        resource.setSysRoles(db.getSysRoles());
        resourceRepository.save(resource);
        return resource;
    }

    /**
     * 根据资源ids 删除用户资源数据
     *
     * @param ids 资源主键集
     * @return 成功或失败数据
     */
    @Transactional
    public List<SysResource> deleteSysResource(List<String> ids) {
        if (ValidatorUtils.isArrayNotEmpty(ids)) {
            List<SysResource> resources = resourceRepository.findAll(ids);
            resourceRepository.delete(resources);
            return resources;
        }
        throw new ServiceException("删除异常");
    }

    /**
     * 角色资源树
     *
     * @param roleId 角色id
     * @return List<Node> 节点列表
     */

    public List<Node> roleResources(String roleId) {
        List<SysResource> li = sysResourceRepository.findAll();
        List<String> selected = new ArrayList<String>();
        SysRole role = sysRoleRepository.findOne(roleId);
        if (role != null && ValidatorUtils.isArrayNotEmpty(role.getSysResources())) {
            for (SysResource sysResource : role.getSysResources()) {
                selected.add(sysResource.getId());
            }
        }
        List<Node> nodes = new ArrayList<Node>();
        for (SysResource r : li) {
            if (r.getAvailable() == 0) {
                break;
            }
            Node node = new Node();
            node.setId(r.getId());
            node.setName(r.getResourceName());
            node.setChecked(selected.contains(r.getId()));
            node.setOpen("true");
            node.setParentId(r.getParentId());
            nodes.add(node);
        }
        return TreeNodeUtils.buildTree(nodes, new TreeNodeHelper<Node>(new TreeNodeHelper.TreeNodeParentHelper<Node>() {

            public String getParentId(Node node) {
                return node.getParentId();
            }
        }));
    }


    /**
     * 角色资源树
     *
     * @param tree        节点
     * @param li          资源列表
     * @param id          角色主键
     * @param orgLi       权限主键列表
     * @param parentResli 父级角色主键列表
     * @return Node 节点
     */
    public Node sonResourceTree(Node tree, List<SysResource> li, String id, List<String> orgLi, List<String> parentResli) {
        List<Node> trees = new ArrayList<Node>();
        for (SysResource subm : li) {
//            if (subm.getAvailable() == 0) {
//                break;
//            }
            Node subtree = new Node();
            subtree.setId(subm.getId());
            subtree.setName(subm.getResourceName());
            if (parentResli != null) {
                if (parentResli.contains(subm.getId())) {
                    subtree.setChecked(true);
                    subtree.setChkDisabled("true");
                }
            }
            if (id != null) {
                if (orgLi.contains(subm.getId())) {
                    subtree.setChecked(true);
                    subtree.setChkDisabled("false");
                }
            }
            if (subm.getResourceType().equals("menu")) {
                subtree.setIsParent(true);
            }
            List<SysResource> sonLi = resourceDao.findByPid(subm.getId());
            subtree = this.sonResourceTree(subtree, sonLi, id, orgLi, parentResli);
            trees.add(subtree);
        }
        tree.setChildren(trees);
        return tree;
    }

    /**
     * 角色资源树
     *
     * @param strngli 资源主键列表
     * @param roleIds 角色主键
     * @return List<String> 权限主键列表
     */
    public List<String> queryParentResOfRole(List<String> strngli, List<String> roleIds) {
        List<String> parentRoleIdLi = null; //= relationRepository.findParentIdOfRoleId(roleIds);
        if (parentRoleIdLi != null && parentRoleIdLi.size() > 0) {
            strngli.addAll(resourceRepository.findIdByRoleIds(parentRoleIdLi));
            strngli = this.queryParentResOfRole(strngli, parentRoleIdLi);
        }
        return strngli;
    }

//    /**
//     * 角色资源树
//     *
//     * @param roleId 角色id
//     * @return List<Node> 节点列表
//     */
//
//    public List<Node> showRoleResourceTree(String roleId) {
//        List<Node> trees = new ArrayList<Node>();
//        List<SysResource> li = sysResourceRepository.findAll();
//        for (SysResource r : li) {
//            if (r.getAvailable() == 0) {
//                break;
//            }
//            Node tree = new Node();
//            tree.setId(r.getId());
//            tree.setName(r.getResourceName());
//            tree.setParentId(r.getParentId());
//            tree.setOpen("true");
//            trees.add(tree);
//        }
//        trees = TreeNodeUtils.buildTree(trees, new TreeNodeHelper<Node>(new TreeNodeHelper.TreeNodeParentHelper<Node>() {
//
//            public String getParentId(Node treeNode) {
//                return treeNode.getParentId();
//            }
//        }));
//        return trees;
//    }

    /**
     * 角色资源树
     *
     * @param roleId 角色id
     * @return List<Node> 节点列表
     */

    public List<Node> showRoleResourceTree(String roleId) {
        List<Node> trees = new ArrayList<Node>();
        List<SysResource> li = resourceDao.findAllOfFather();
        List<String> orgLi = new ArrayList<String>();
        List<String> parentResli = new ArrayList<String>();
        if (!roleId.equals("")) {
            orgLi = resourceRepository.findByRoleId(roleId);
            List<String> roleIdLis = new ArrayList<String>();
            roleIdLis.add(roleId);
            parentResli = this.queryParentResOfRole(parentResli, roleIdLis);
        }
        for (SysResource r : li) {
            if (r.getAvailable() == 0) {
                break;
            }
            Node tree = new Node();
            tree.setId(r.getId());
            tree.setName(r.getResourceName());
            if (parentResli.contains(r.getId())) {
                tree.setChecked(true);
                tree.setChkDisabled("true");
            }
            if (orgLi.contains(r.getId())) {
                tree.setChecked(true);
                tree.setChkDisabled("false");
            }
            tree.setOpen("true");
            if (r.getResourceType().equals("menu")) {
                tree.setIsParent(true);
            }
            List<SysResource> sonLi = resourceDao.findByPid(r.getId());
            tree = this.sonResourceTree(tree, sonLi, roleId, orgLi, parentResli);
            trees.add(tree);
        }
        return trees;
    }

    /**
     * 显示用户资源
     *
     * @param tree    节点树
     * @param li      资源列表
     * @param subject 权限
     * @return Node 节点
     */
    public Node sonUserResourceTree(Node tree, List<SysResource> li, Subject subject) {
        List<Node> trees = new ArrayList<Node>();
        for (SysResource subm : li) {
            if (subject.isPermitted(subm.getPermission()) && subm.getResourceType().equals("menu")) {
                Node subtree = new Node();
                subtree.setId(subm.getId());
                subtree.setName(subm.getResourceName());
                subtree.setText(subm.getResourceName());
                subtree.setAlias(subm.getResourceUrl());
                if (subm.getResourceUrl() == null || subm.getResourceUrl().trim().equals("")) {
                    subtree.setLeaf(false);
                } else {
                    subtree.setLeaf(true);
                }
                List<SysResource> sonLi = resourceDao.findByPid(subm.getId());
                subtree = this.sonUserResourceTree(subtree, sonLi, subject);
                trees.add(subtree);
            }
        }
        tree.setChildren(trees);
        return tree;
    }

    public Page<SysResource> showAllMenu(SearchResourceCondition searchResourceCondition, Pageable pageable) {
        final SearchResourceCondition condition = searchResourceCondition == null
                ? new SearchResourceCondition()
                : searchResourceCondition;
        Specification<SysResource> sysResourceSpecification = SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysResource>() {

            public void fillParam(Root<SysResource> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("resourceType").as(String.class), "menu"));
                list.add(cb.equal(root.get("available").as(Integer.class), 1));
                if (org.apache.commons.lang.StringUtils.isNotBlank(condition.getResourceName())) {
                    list.add(cb.like(root.get("resourceName").as(String.class), "%" + condition.getResourceName() + "%"));
                }
            }
        });
        return sysResourceRepository.findAll(sysResourceSpecification, pageable);
    }

    /**
     * 查询所有菜单
     *
     * @param userid 用户主键
     * @return List<Node> 节点列表
     */

    public List<Node> showUserResourceAll(String userid) {
        //查找用户所拥有的资源。
        //用户所拥有的资源的ID集合
        List<String> userMenuList = new ArrayList<String>();
        List<Node> trees = new ArrayList<Node>();
        //查找资源的根列表。
        final List<SysResource> li = resourceRepository.findAll(SpecificationHelper.buildSpecification(new SpecificationHelper.SpecificationBuilder<SysResource>() {

            public void fillParam(Root<SysResource> root, CriteriaBuilder cb, List<Predicate> list) {
                list.add(cb.equal(root.get("available").as(Integer.class), 1));
            }
        }));
        Set<String> setUserResource = new HashSet<String>();
        SysUserMember sum = sysUserRepository.findOne(userid);
        //查找用户拥有的所有角色。
        List<SysRole> list = sum.getSysRoles();
        Set<String> listRoleId = new HashSet<String>();
        for (SysRole sr : list) {
            listRoleId.add(sr.getId());
        }
        for (String roleId : listRoleId) {
            //查找角色所拥有的资源。
            setUserResource.addAll(resourceRepository.findByRoleId(roleId));
        }
        for (SysResource r : li) {
            Node tree = new Node();
            tree.setOpen("true");
            tree.setId(r.getId());
            tree.setName(r.getResourceName());
            tree.setText(r.getResourceName());
            tree.setParentId(r.getParentId());
            //用户继承的资源
            if (setUserResource.contains(r.getId())) {
                tree.setChecked(true);
                tree.setExpanded("true");
                tree.setChkDisabled("true");
            }
            //用户所拥有的资源
            if (userMenuList.contains(r.getId())) {
                tree.setChecked(true);
                tree.setExpanded("true");
                tree.setChkDisabled("false");
            }
            tree.setIsParent(r.getResourceType().equals("menu"));
            trees.add(tree);
        }
        List<Node> results = TreeNodeUtils.buildTree(trees, new TreeNodeHelper<Node>(new TreeNodeHelper.TreeNodeParentHelper<Node>() {

            public String getParentId(Node treeNode) {
                return treeNode.getParentId();
            }
        }));
        TreeNodeUtils.ergodicTree(results, new TreeNodeHelper<Node>(new TreeNodeHelper.TreeNodeOperateHelper<Node>() {

            public void doOperate(Node treeNode) {
                treeNode.setOpen("false");
                treeNode.setExpanded("false");
            }
        }));
        return results;
    }

    /**
     * 查询所有菜单
     *
     * @param tree            节点
     * @param li              资源列表
     * @param userMenuList    用户菜单列表
     * @param setUserResource 用户角色资源集
     * @return Node 节点
     */
    public Node sonUserResourceTreeAll(Node tree, List<SysResource> li, List<String> userMenuList, Set<String> setUserResource) {
        List<Node> trees = new ArrayList<Node>();
        for (SysResource subm : li) {
            Node subtree = new Node();
            subtree.setId(subm.getId());
            subtree.setName(subm.getResourceName());
            subtree.setText(subm.getResourceName());
            if (setUserResource.contains(subm.getId())) {
                subtree.setChecked(true);
                subtree.setExpanded("true");
                subtree.setChkDisabled("true");
            }
            if (userMenuList.contains(subm.getId())) {
                subtree.setChecked(true);
                subtree.setExpanded("true");
                subtree.setChkDisabled("false");
            }
            if (subm.getResourceType().equals("menu")) {
                subtree.setIsParent(true);
            }
            List<SysResource> sonLi = resourceDao.findByPid(subm.getId());
            subtree = this.sonUserResourceTreeAll(subtree, sonLi, userMenuList, setUserResource);
            trees.add(subtree);
        }
        tree.setChildren(trees);
        return tree;
    }

    /**
     * 查找某一个资源
     *
     * @param id 资源主键
     * @return SysResource 资源
     */

    public SysResource findOne(String id) {
        return StringUtils.isBlank(id) ? null : resourceRepository.findOne(id);
    }


    /**
     * 查找某一个资源
     *
     * @param permission 资源主键
     * @return SysResource 资源
     */

    public SysResource findByPermission(String permission) {
        if(StringUtils.isBlank(permission)){
            return null;
        }
        List<SysResource> sysResources =  resourceRepository.findResourceByPermission(permission);
        return sysResources.isEmpty() ? null : sysResources.get(0);
    }

    /**
     * 新增资源时重复资源名称验证
     *
     * @param resourceName 资源名称
     * @return 布尔值 true:相同 false:不同
     */
    public boolean repeatValidate(String resourceName) {
        SysResource us = resourceRepository.repeatValidate(resourceName);
        return us != null;
    }

    /**
     * 修改资源时重复资源名称验证
     *
     * @param resourceName 资源名称
     * @param id           资源主键
     * @return 布尔值 true:相同 false:不同
     */
    public boolean repeatValidate(String resourceName, String id) {
        SysResource us = resourceRepository.repeatValidate(resourceName, id);
        return us != null;
    }

    public boolean hasPermission(String key){
        if(StringUtils.isBlank(key)){
            return true;
        }
        List<SysResource> resources = AuthContextHelper.getLoginUser().getSysResources();
        for(SysResource sysResource : resources){
            if(key.equals(sysResource.getPermission())){
                return true;
            }
        }
        return false;
    }

    public Set<String> findUserPermissions(String userId, Set<String> roleNames) {
        Set<String> idLi = new HashSet<String>();
//        第一步获取用户拥有的角色
        SysUserMember sysUserMember = sysUserRepository.findOne(userId);
        List<SysRole> sysRoles = sysUserMember.getSysRoles();
        if (ValidatorUtils.isArrayNotEmpty(sysRoles)) {
            for (SysRole sysRole : sysRoles) {
                roleNames.add(sysRole.getRoleName());
                idLi.add(sysRole.getId());
            }
        }
        //获取用户的所有部门拥有的角色
        final Set<String> permissions = new HashSet<String>();
        final Set<String> result = new HashSet<String>();
        if (ValidatorUtils.isArrayNotEmpty(idLi)) {
            permissions.addAll(sysResourceRepository.findByRoleIds(new ArrayList<String>(idLi)));
        }
        List<SysResource> sysResources = sysResourceRepository.findAll();
        //构造属性，过滤
        sysResources = TreeNodeUtils.buildTree(sysResources, new TreeNodeHelper<SysResource>(new TreeNodeHelper.TreeNodeParentHelper<SysResource>() {

            public String getParentId(SysResource sysResource) {
                return sysResource.getParentId();
            }
        }));
        TreeNodeUtils.ergodicTree(sysResources, new TreeNodeHelper<SysResource>(new TreeNodeHelper.TreeNodeParentValidatorHelper<SysResource>() {

            public boolean doValidate(SysResource sysResource) {
                return org.apache.commons.lang.StringUtils.isNotBlank(sysResource.getPermission()) && permissions.contains(sysResource.getPermission());
            }
        }, new TreeNodeHelper.TreeNodeOperateHelper<SysResource>() {

            public void doOperate(SysResource sysResource) {
                if (org.apache.commons.lang.StringUtils.isNotBlank(sysResource.getPermission())) {
                    result.add(sysResource.getPermission());
                }
            }
        }));
        return result;
    }

    /**
     * 生成站点地图
     *
     * @param url
     * @param resources
     * @return
     */
    public List<Site> getSites(String url, String params, List<SysResource> resources) {
        //找到匹配的url路径
        List<Site> sites = new ArrayList<Site>();
        if (StringUtils.isNotBlank(url)) {
            for (SysResource sysResource : resources) {
                if (StringUtils.isNotBlank(sysResource.getResourceUrl()) && url.contains(sysResource.getResourceUrl().trim())) {
                    SysResource _source = sysResource;
                    _source.setResourceUrl(url + (StringUtils.isBlank(params) ? "" : ("?" + params)));
                    while (_source != null) {
                        Site site = new Site();
                        site.setName(_source.getResourceName());
                        site.setId(_source.getId());
                        site.setLink(_source.getResourceUrl());
                        sites.add(site);
                        _source = findOne(_source.getParentId());
                    }
                    Collections.reverse(sites);
                    return sites;
                }
            }
        }
        return Collections.emptyList();
    }
}
